/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: Direct uart driver 
 * low-level driver routines for 16550a UART.
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2021-10-1      JasonHu           Init
 */

#include <nxos_mini.h>
#include <base/delay_irq.h>
#include <drivers/direct_uart.h>
#include <base/console.h>
#define NX_LOG_NAME "PL011 UART"
#include <base/log.h>
#include <base/debug.h>
#include <arch/base.h>

#define __REG32(x)          (*((volatile unsigned int *)(x)))

#define UART_DR(base)   __REG32(base + 0x00)
#define UART_FR(base)   __REG32(base + 0x18)
#define UART_LCRH(base) __REG32(base + 0x2c)
#define UART_CR(base)   __REG32(base + 0x30)
#define UART_IFLS(base) __REG32(base + 0x34)
#define UART_IMSC(base) __REG32(base + 0x38)
#define UART_ICR(base)  __REG32(base + 0x44)

#define UARTFR_RXFE     0x10
#define UARTFR_TXFF     0x20
#define UARTIMSC_RXIM   0x10
#define UARTIMSC_TXIM   0x20
#define UARTICR_RXIC    0x10
#define UARTICR_TXIC    0x20

/* UART */

#define PL011_UART0_BASE       (PBASE+0x00201000)

#define PL011_UART0_SIZE    0x00001000
#define PL011_UART0_IRQNUM  (96 + 57)

#define UART0_BASE PL011_UART0_BASE

void NX_HalDirectUartPutc(char ch)
{
    int i = 1000000000;
    while ((UART_FR(UART0_BASE) & UARTFR_TXFF) && (i-- > 0));
    UART_DR(UART0_BASE) = ch;
}

int NX_HalDirectUartGetc(void)
{
    int ch;
    ch = -1;
    
    while ((UART_FR(UART0_BASE) & UARTFR_RXFE) != 0);
    ch = UART_DR(UART0_BASE) & 0xff;
    
    return ch;
}

NX_INTERFACE void NX_ConsoleSendData(char ch)
{
    if (ch == '\n')
    {
        NX_HalDirectUartPutc('\r');
    }
    NX_HalDirectUartPutc(ch);
}

void NX_HalDirectUartInit(void)
{
    /* initialize UART0 */
    
    /* disable fifo */
    UART_LCRH(UART0_BASE) &= ~(1u << 4);

    /* set Receive interrupt FIFO level becomes 1/8 */
    UART_IFLS(UART0_BASE) = 0x02; /* tx: 0x02(default), rx: 0x00(1/8) */

    /* enable Rx and Tx of UART */
    UART_CR(UART0_BASE) = (1 << 0) | (1 << 8) | (1 << 9);
}

/**
 * default handler
*/
NX_WEAK_SYM void NX_HalDirectUartGetcHandler(char data)
{
    NX_ConsoleReceveData(data);
    // NX_Printf("\n->rx:%c/%x\n", data, data);
}

NX_PRIVATE NX_Error UartIrqHandler(NX_IRQ_Number irqno, void *arg)
{
    int data = NX_HalDirectUartGetc();
    if (data != -1)
    {
        if (NX_HalDirectUartGetcHandler != NX_NULL)
        {
            NX_HalDirectUartGetcHandler(data);
        }
    }
    return data != -1 ? NX_EOK : NX_EIO;
}

void NX_HalDirectUartStage2(void)
{
    NX_ASSERT(NX_IRQ_Bind(PL011_UART0_IRQNUM, UartIrqHandler, NX_NULL, "Uart", 0) == NX_EOK);
    NX_ASSERT(NX_IRQ_Unmask(PL011_UART0_IRQNUM) == NX_EOK);
    
    /* enable uart rx interrupt */
    UART_IMSC(UART0_BASE) = UARTIMSC_RXIM;
}
